#include <stdexcept>
#include "Math.h"
#include "Vector.h"


Vector::Vector (const size_t n){
	dimension = n;
	data = new double[n];
	for( size_t i = 0; i < n; i++) {
		data[i] = 0.0;
	}
}

Vector::Vector (const size_t n, const double* another){
	if( NULL == another ) {
		throw std::invalid_argument ("Pointer to another data cannot be NULL");
	}
	dimension = n;
	data = new double[n];
	
	for ( size_t i = 0; i < n; i++) {
		data[i] = another[i];
	}
}

Vector::Vector (const Vector& another){
	dimension = another.dimension;
	data = new double[dimension];
	
	for ( size_t i = 0; i < dimension; i++){
		data[i] = another.data[i];
	}
}

Vector::Vector (const size_t n, const Vector& another){
	dimension = n;
	data = new double[n];
	size_t dataBarrier = ( dimension > another.dimension ) ? another.dimension : dimension;
	
	for ( size_t i = 0; i < dataBarrier; i++ ) {
		data[i] = another.data[i];
	}
	
	for ( size_t i = dataBarrier; i < dimension; i++ ) {
		data[i] = 0.0;
	}
}

Vector::~Vector() {
	delete[] data;
};

size_t Vector::size() const {
	return dimension;
}

bool Vector::isIndexValid ( const size_t index ) const {
	return ( index >= 0 && index < dimension );
}

double& Vector::operator[](const size_t index) const {
	if( !isIndexValid(index) ){
		throw std::out_of_range("");
	}
	return data[index];
}
double& Vector::operator[](const size_t index){
	if( !isIndexValid(index) ){
		throw std::out_of_range("");
	}
	return data[index];
}

Vector& Vector::operator= (const Vector& another)
{
	
	if( this == &another )
	{
		return *this;
	}

	if (dimension != another.dimension)
	{
		delete[] data;
		dimension = another.dimension;
		data = new double[dimension];
	}
		for ( size_t i = 0; i < dimension; i++ )
		{
			data[i] = another.data[i];
		}
	
	return *this;
}

Vector& Vector::operator+= (const Vector& another)
{
	if (dimension < another.dimension)
	{	
		double *newData = new double[another.dimension];
		for(size_t i = 0; i < dimension; i++) {
			newData[i] = data[i];
		}
		for( size_t i = dimension; i < another.dimension; i++) {
			newData[i] = 0.0;
		}
		delete[] data;
		data = newData;
		dimension = another.dimension;
	}

	for ( size_t i = 0; i < dimension; i++ )
	{
		data[i] += another.data[i];
	}
	return *this;
}


Vector& Vector::operator*= (const double constant)
{
	for (size_t i = 0; i < this->dimension; i++)
	{
		data[i] *= constant;
	}

	return *this;
}

Vector Vector::invering (Vector& another)
{
	return (another * -1);
}

Vector& Vector::operator-= (Vector& another)
{
	*this += invering (another);
	return *this;
}


double Vector::operator*(const Vector& second) const
{
	size_t mindimenion = Math::min<size_t>(dimension, second.dimension);
	double result = 0.0;
	for ( size_t i = 0; i < mindimenion; i++ )
	{
		result += (data[i] * second.data[i]);
	}
	
	return result;
}

Vector operator* (double constant, Vector& another)
{
	return another * constant;
}

Vector Vector::operator* (double constant) const
{
	Vector result (*this);
	for (size_t i = 0; i < dimension; i++)
	{
		result.data[i] *= constant;
	}
	return result;
}

Vector Vector::operator+(Vector& second) const
{
	size_t maxDimension = Math::max (dimension, second.dimension);
	Vector result (maxDimension,*this);
	
	for (size_t i = 0; i < second.dimension; i++)
	{
		result.data[i] += second.data[i];
	}
	return result;
}

Vector Vector::operator-(Vector& second) const
{
	size_t maxDimension = Math::max (dimension, second.dimension);
	Vector result (maxDimension,*this);
	
	for (size_t i = 0; i < second.dimension; i++)
	{
		result.data[i] -= second.data[i];
	}
	return result;
}

void Vector::exchange(size_t firstIndex, size_t secondIndex) {
	if( !isIndexValid(firstIndex) || !isIndexValid(secondIndex) ) {
		throw std::invalid_argument("Exchange indexes are invalid");
	}
	double tmp = data[firstIndex];
	data[firstIndex] = data[secondIndex];
	data[secondIndex] = tmp;
}

std::ostream& operator << (std::ostream& outputStream, const Vector& vector) {

	size_t dimension = vector.size();
	outputStream << "{";
	
	for( size_t i = 0; i < dimension - 1; i++ ) {
		outputStream << vector[i] << ", ";
	}
	if (dimension > 0)
	{
		outputStream << vector[dimension - 1];
	}
	outputStream << "}";
	return outputStream;
}

std::istream& operator >> (std::istream& inputStream, Vector& vector)
{
	size_t dimension = vector.size();

	if( 0 == dimension ) {
		return inputStream;
	}

	Vector vectorToRead (dimension);

	char  vectorSeparator;
	inputStream >> vectorSeparator;
	if( inputStream.fail() || vectorSeparator != '{' ) {
		throw std::invalid_argument ("Vector begin is not {");
	}

	char delimiter;
	for(size_t i = 0; i < dimension - 1; i++ ) {
		inputStream >> vectorToRead[i];
		inputStream >> delimiter;
		if( inputStream.fail() || delimiter != ',' ) {
			throw std::invalid_argument ("Vector components must have a delimiter ,");
		}
	}
	inputStream >> vectorToRead[dimension - 1];
	inputStream >> vectorSeparator;
	if( inputStream.fail() || vectorSeparator != '}' ) {
		throw std::invalid_argument ("Vector end is not }");
	}

	vector = vectorToRead;
	return inputStream;
}